package com.crx.springbootinit.aop;

import com.alibaba.fastjson.JSON;
import com.crx.springbootinit.annotation.LogApiAnno;
import com.crx.springbootinit.model.entity.LogApi;
import com.crx.springbootinit.service.ILogApiService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;

/**
 * @author 谢杭延
 * @Date 2024-10-28 14:18
 */
@Aspect
@Component
@Slf4j
public class LogApiAspect {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ILogApiService logApiService;
    @Pointcut("@annotation(com.crx.springbootinit.annotation.LogApiAnno)")
    public void logPointCut(){
    }

    /**
     * 处理完请求后执行
     *
     * @param joinpoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinpoint, Object jsonResult) {
        handleLog(joinpoint,null,jsonResult);
    }

    /**
     * 拦截异常操作
     * @param joinpoint
     * @param e
     */
    @AfterThrowing(pointcut = "logPointCut()", throwing = "e")
    public void doAfterTrowing(JoinPoint joinpoint,Exception e){
        handleLog(joinpoint,e,null);
    }

    @Async("taskExecutor")
    protected void handleLog(final JoinPoint joinpoint,final Exception e,Object jsonResult){

        try {
            LogApiAnno controllerLogApiAnno =getAnnotationLog(joinpoint);
            if(controllerLogApiAnno ==null){
                return;
            }
            //获取请求的url
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
            StringBuffer url = httpServletRequest.getRequestURL();
            //获取当前用户id
            String token = httpServletRequest.getHeader("token");;
            if(token==null){
                return;
            }
            Long userId=(Long) redisTemplate.opsForValue().get(token);

            //获取ip地址
            String clientId=httpServletRequest.getHeader("X-Forwarded-For");
            if(clientId==null||clientId.isEmpty()||"unknown".equals(clientId)){
                clientId=httpServletRequest.getHeader("X-Real-IP");
            }
            if(clientId==null||clientId.isEmpty()||"unknown".equals(clientId)){
                clientId=httpServletRequest.getRemoteHost();
            }
            String jsonResultV = JSON.toJSONString(jsonResult);
            if(e!=null){

                log.info("异常信息:{}",e.getMessage());
            }
            //设置方法名
            String className = joinpoint.getTarget().getClass().getName();
            String methodName = joinpoint.getSignature().getName();
            //处理方法参数
            Object[] args = joinpoint.getArgs();
            String argsV = argsToString(args);
            //处理注解上的参数
            String businessType = controllerLogApiAnno.businessType();
            String title = controllerLogApiAnno.title();
            //保存数据库
            String id= UUID.randomUUID().toString();
            LocalDateTime currentTime=LocalDateTime.now();
            LogApi logApi = LogApi.builder()
                    .id(id)
                    .userId(userId)
                    .ip(clientId)
                    .requestPath(url.toString())
                    .module(title)
                    .requestType(businessType)
                    .operationTime(currentTime)
                    .methodName(className+"."+methodName+"()")
                    .params(argsV)
                    .result(jsonResultV)
                    .build();
            if(e!=null){
                logApi.setResult(e.getMessage());
                log.info("异常信息:{}",e.getMessage());
            }
            logApiService.save(logApi);
            log.info("用户id:{},用户ip:{},请求地址:{},业务模块:{},请求类型:{},操作时间:{},方法名称:{},请求参数:{},返回结果:{}",userId,clientId,
                    url,title,businessType, currentTime,className+"."+methodName+"()",argsV,jsonResultV);
        }catch (Exception exp){
            log.error("==前置异常通知==");
            log.error("异常信息:{}"+exp.getMessage());
            exp.printStackTrace();
        }

    }

    /**
     * 是否存在注解，如果存在就获取
     *
     */
    private LogApiAnno getAnnotationLog(JoinPoint joinpoint) {
        //是否存在注解，存在就获取，先获取该方法，在从该方法上获取注解
        Signature signature = joinpoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if(method!=null){
            return method.getAnnotation(LogApiAnno.class);
        }
        return null;
    }

    /**
     * 请求参数拼装
     */
    private String argsToString(Object[] paramsArray) {
        String params = "";
        if(paramsArray!=null&&paramsArray.length>0){
            for(int i=0;i<paramsArray.length;i++){
                if(!isFilterObject(paramsArray[i])){
                    Object json = JSON.toJSON(paramsArray[i]);
                    params += json.toString()+" ";
                }
            }
        }
        return params.trim();
    }

    /**
     *   判断是否需要过滤的对象
     * @param o
     * @return
     */
    public boolean isFilterObject(final Object o){
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }

}
